﻿using DevExpress.Mvvm.POCO;
using Habi.Solver.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetExtensions.AspNet.HttpClients;
using DotNetExtensions.AspNet.DevExpress.Services;

namespace Habi.Solver.ViewModels
{
    public class RandomViewModel
    {
        public virtual List<RandomModel> Red_T { get; set; }
        public virtual List<RandomModel> Blue_T { get; set; }

        public virtual List<RandomModel> Red_S { get; set; }
        public virtual List<RandomModel> Blue_S { get; set; }

        public virtual List<RandomResult> ResultT { get; set; }
        public virtual List<RandomResult> ResultS { get; set; }

        public virtual bool Runing { get; set; }

        private IClipboardService ClipboardService { get { return this.GetService<IClipboardService>(); } }

        private IHttpClient httpClient = HttpClientFactory.Default.Create(HttpClientFactory.HttpClientKind.Restful);

        public RandomViewModel()
        {
            ResultT = new List<RandomResult>();
            ResultS = new List<RandomResult>();
        }

        public void Load()
        {
            Runing = true;
        }

        public void Tick()
        {
            var resBodyT = httpClient.Get($"http://192.168.20.217:5678/api/Gen/Random/0").To<IndexChartDataModel>();
            if (resBodyT != null && resBodyT.Code == 200)
            {
                var data = resBodyT.Data;

                Red_T = data.Red.ToList();
                Blue_T = data.Blue.ToList();
                Compute(data, "T");
            }

            var resBodyS = httpClient.Get($"http://192.168.20.217:5678/api/Gen/Random/1").To<IndexChartDataModel>();
            if (resBodyS != null && resBodyS.Code == 200)
            {
                var data = resBodyS.Data;

                Red_S = data.Red.ToList();
                Blue_S = data.Blue.ToList();
                Compute(data, "S");
            }
        }

        private string ComputeMax(IndexChartDataModel data, string type)
        {
            string red = ComputeMax(data.Red.Numbers, type == "S" ? 6 : 5);
            string blue = ComputeMax(data.Blue.Numbers, type == "S" ? 1 : 2);

            return $"{red}+{blue}";
        }

        private string ComputeMin(IndexChartDataModel data, string type)
        {
            string red = ComputeMin(data.Red.Numbers, type == "S" ? 6 : 5);
            string blue = ComputeMin(data.Blue.Numbers, type == "S" ? 1 : 2);

            return $"{red}+{blue}";
        }

        private string ComputeAvg(IndexChartDataModel data, string type)
        {
            string red = ComputeAvg(data.Red.Numbers, type == "S" ? 6 : 5);
            string blue = ComputeAvg(data.Blue.Numbers, type == "S" ? 1 : 2);

            return $"{red}+{blue}";
        }

        private void Compute(IndexChartDataModel data, string type)
        {
            RandomResult ret = new RandomResult();
            ret.Type = type;
            ret.Avg = ComputeAvg(data, type);
            ret.Max = ComputeMax(data, type);
            ret.Min = ComputeMin(data, type);

            if (type == "T" && !ResultT.Any(r => r.Avg == ret.Avg && r.Max == ret.Max && r.Min == ret.Min))
            {
                ResultT = ResultT.Concat(new RandomResult[] { ret }).ToList();
            }
            if (type == "S" && !ResultS.Any(r => r.Avg == ret.Avg && r.Max == ret.Max && r.Min == ret.Min))
            {
                ResultS = ResultS.Concat(new RandomResult[] { ret }).ToList();
            }
        }

        private string ComputeAvg(int[] numbers, int len)
        {
            double avg = numbers.Average();

            List<Avg> avgs = new List<Avg>();
            for (int i = 0; i < numbers.Length; i++)
            {
                int n = numbers[i];
                Avg a = new Avg();
                a.Distance = Math.Abs(n - avg);
                a.Number = i + 1;

                avgs.Add(a);
            }

            int[] newNumbers = avgs.OrderBy(a => a.Distance).Take(len).Select(a => a.Number).ToArray();
            Array.Sort(newNumbers);
            return string.Join(",", newNumbers);
        }

        private string ComputeMax(int[] numbers, int len)
        {
            double avg = numbers.Average();

            List<Avg> avgs = new List<Avg>();
            for (int i = 0; i < numbers.Length; i++)
            {
                int n = numbers[i];
                Avg a = new Avg();
                a.Distance = n - avg;
                a.Number = i + 1;

                avgs.Add(a);
            }

            int[] newNumbers = avgs.OrderByDescending(a => a.Distance).Take(len).Select(a => a.Number).ToArray();
            Array.Sort(newNumbers);
            return string.Join(",", newNumbers);
        }

        private string ComputeMin(int[] numbers, int len)
        {
            double avg = numbers.Average();

            List<Avg> avgs = new List<Avg>();
            for (int i = 0; i < numbers.Length; i++)
            {
                int n = numbers[i];
                Avg a = new Avg();
                a.Distance = n - avg;
                a.Number = i + 1;

                avgs.Add(a);
            }

            int[] newNumbers = avgs.OrderBy(a => a.Distance).Take(len).Select(a => a.Number).ToArray();
            Array.Sort(newNumbers);
            return string.Join(",", newNumbers);
        }

        public void Copy()
        {
            string t = ResultT.LastOrDefault()?.ToString();
            string s = ResultS.LastOrDefault()?.ToString();
            if (!string.IsNullOrEmpty(t) || !string.IsNullOrEmpty(s))
            {
                ClipboardService.SetText($"{t}\r\n{s}");
            }
        }

        public void Ignore()
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine(ExcludeT());
            sb.AppendLine("\r\n");
            sb.AppendLine(ExcludeS());

            ClipboardService.SetText(sb.ToString());
        }

        private string ExcludeT()
        {
            string nums = "";
            List<int> allNums = new List<int>();

            for (int i = 0; i < 35; i++)
            {
                allNums.Add(i + 1);
            }
            int lastIndex = ResultT.Count - 1;
            do
            {
                var sltNums = GetNums(ResultT[lastIndex], 0);
                foreach (var item in sltNums)
                {
                    if (allNums.Count > 5) allNums.Remove(item);
                }

                lastIndex -= 1;
            } while (allNums.Count > 5 && lastIndex >= 0);

            List<int> allNums2 = new List<int>();
            for (int i = 0; i < 12; i++)
            {
                allNums2.Add(i + 1);
            }
            int lastIndex2 = ResultT.Count - 1;
            do
            {
                var sltNums = GetNums(ResultT[lastIndex2], 1);
                foreach (var item in sltNums)
                {
                    if (allNums2.Count > 2) allNums2.Remove(item);
                }
                lastIndex2 -= 1;
            } while (allNums.Count > 2 && lastIndex2 >= 0);

            if (allNums.Count >= 5 && allNums2.Count >= 2)
            {
                nums += string.Join(",", GetSubList(allNums, 5));
                nums += "+";
                nums += string.Join(",", GetSubList(allNums2, 2));
            }

            return nums;
        }

        private string ExcludeS()
        {
            string nums = "";
            List<int> allNums = new List<int>();

            for (int i = 0; i < 33; i++)
            {
                allNums.Add(i + 1);
            }
            int lastIndex = ResultS.Count - 1;
            do
            {
                var sltNums = GetNums(ResultS[lastIndex], 0);
                foreach (var item in sltNums)
                {
                    if (allNums.Count > 6) allNums.Remove(item);
                }

                lastIndex -= 1;
            } while (allNums.Count > 6 && lastIndex >= 0);

            List<int> allNums2 = new List<int>();
            for (int i = 0; i < 16; i++)
            {
                allNums2.Add(i + 1);
            }
            int lastIndex2 = ResultS.Count - 1;
            do
            {
                var sltNums = GetNums(ResultS[lastIndex2], 1);
                foreach (var item in sltNums)
                {
                    if (allNums2.Count > 1) allNums2.Remove(item);
                }

                lastIndex2 -= 1;
            } while (allNums.Count > 1 && lastIndex2 >= 0);

            if (allNums.Count >= 6 && allNums2.Count >= 1)
            {
                nums += string.Join(",", GetSubList(allNums, 6));
                nums += "+";
                nums += string.Join(",", GetSubList(allNums2, 1));
            }

            return nums;
        }

        private List<int> GetSubList(List<int> data, int count)
        {
            if (data.Count < count)
            {
                return data;
            }
            Random random;
            List<int> tempList = new List<int>();
            List<int> newList = new List<int>();
            int temp = 0;
            for (int i = 0; i < count; i++)
            {
                random = new Random();
                temp = random.Next(data.Count);
                if (!tempList.Contains(temp))
                {
                    tempList.Add(temp);
                    newList.Add(data[temp]);
                }
                else
                {
                    i--;
                }
            }
            return newList;
        }

        private List<int> GetNums(RandomResult result, int step)
        {
            List<int> sltNumsMax = result.Max.Split('+')[step].Split(',').Select(c => int.Parse(c)).ToList();
            List<int> sltNumsMin = result.Min.Split('+')[step].Split(',').Select(c => int.Parse(c)).ToList();
            List<int> sltNumsAvg = result.Avg.Split('+')[step].Split(',').Select(c => int.Parse(c)).ToList();

            return sltNumsAvg.Concat(sltNumsMax).Concat(sltNumsMin).ToList();
        }

        public void Start()
        {
            Runing = true;
        }

        public void Stop()
        {
            Runing = false;
        }
    }

    public class Avg
    {
        public double Distance { get; set; }
        public int Number { get; set; }
    }
}
